#include <iostream>
#include <sstream>

#include "TagDef.h"

#define CANT_TIPOS 4
#define CANT_VALORES 5
#define CANT_ATRIBUTOS 2

using namespace std;

void inspect(TagDef* tagDef, int tabs){
	IteradorLista<AttDef>* itAtributos;
	IteradorLista<TagDef>* itHijosPosibles;
	AttDef* atributo;
	int aux;

	//obtengo un iterador para recorrer los hijos posibles del tag
	itHijosPosibles = new IteradorLista<TagDef>(tagDef->getHijosPosibles());

	for(int i=0; i<tabs; i++)
		cout << "\t";

	//muestro el nombre del tag def
	cout << tagDef->getNombre() <<  endl;

	//obtengo los atributos
	itAtributos = new IteradorLista<AttDef>(tagDef->getAtributos());

	while(itAtributos->tieneSiguiente()){
		atributo = itAtributos->siguiente();
		for(int i=0; i<tabs; i++)
		cout << "\t";

		cout << atributo->getNombre() << ": " << atributo->getTipo()->getNombre() << endl;
	}

	for(int i=0; i<tabs; i++)
		cout << "\t";

	cout << "{" << endl;

	while(itHijosPosibles->tieneSiguiente())
		inspect(itHijosPosibles->siguiente(), tabs + 1);

	for(int i=0; i<tabs; i++)
		cout << "\t";

	cout << "}" << endl;
}

/* Las tres funciones definidas a continuacion reciben un puntero a string (disfrazado como puntero a void) y devuelven verdadero o false de acuerdo si el string recibido puede ser convertido en un tipo de dato particular */

/**
 * Verifica si el string recibido puede ser convertido en un caracter
 */
bool valCar(void* str){
	return ((string*)str)->length() == 1;
}

/**
 * Verifica si el string recibido puede ser convertido en un numero entero
 */
bool valInt(void* str){
	stringstream sStream(*((string*)str));
	int aux;
	bool isValid;

	if((sStream >> aux).fail())
		isValid = false;
	else
		isValid = true;

	return isValid;
}

/**
 * Verifica si el string recibido puede ser convertido en un numero entero positivo
 */
bool valPosInt(void* str){
	stringstream sStream(*((string*)str));
	int aux;
	bool isValid;

	if((sStream >> aux).fail())
		isValid = false;
	else
		isValid = true;

	return isValid && aux > 0;
}

/**
 * Si partimos de que siempre estamos analizando strings para ver si pueden convertirse en caracteres, enteros, etc, no hace falta validar en el caso de que yo me conforme con que solo sea una cadena de caracteres
 */
bool valStr(void* str){
	return true;
}

/**
 * Main de prueba para definiciones de tipo, tag y atributo
 */
int mainu(){

	//declaro una variable auxiliar para hacer un cin y poder pausar el programa para ver la pantalla
	int aux;

	//instancio tres tipos de dato: caracter, entero y entero positivo
	TypeDef chrTypeDef("caracter", &valCar);
	TypeDef intTypeDef("entero", &valInt);
	TypeDef posIntTypeDef("entero positivo", &valPosInt);
	TypeDef strTypeDef("string", &valStr);

	//creo un array de 5 strings para guardar valores
	string valores[CANT_VALORES];
	valores[0] = "a";
	valores[1] = "1";
	valores[2] = "0";
	valores[3] = "-4";
	valores[4] = "aab";

	//creo un array de punteros a TypeDef para guardar los tipos creados
	TypeDef* tipos[CANT_TIPOS];
	tipos[0] = &chrTypeDef;
	tipos[1] = &intTypeDef;
	tipos[2] = &posIntTypeDef;
	tipos[3] = &strTypeDef;

	//creo tres atributos, nombre, ancho y alto y los guardo en un array
	AttDef attDefNombre("nombre", &strTypeDef);
	AttDef attDefAlto("alto", &posIntTypeDef);

	AttDef* atributos[CANT_ATRIBUTOS];
	atributos[0] = &attDefNombre;
	atributos[1] = &attDefAlto;

	//creo una definicion de tag
	TagDef* tagEscenario = new TagDef("escenario", true, true, true); 
	tagEscenario->getAtributos()->agregar(new AttDef("nombre", &strTypeDef));
	
	TagDef* tagGrilla = new TagDef("grilla", true, true, true);
	tagEscenario->getHijosPosibles()->agregar(tagGrilla);
	tagGrilla->getAtributos()->agregar(new AttDef("alto", &posIntTypeDef));
	tagGrilla->getAtributos()->agregar(new AttDef("ancho", &posIntTypeDef));
	
	TagDef* tagCamino = new TagDef("camino", true, true, true);
	tagGrilla->getHijosPosibles()->agregar(tagCamino);
	tagCamino->getAtributos()->agregar(new AttDef("fila", &intTypeDef));
	tagCamino->getAtributos()->agregar(new AttDef("columna", &intTypeDef));

	//prueba de TypeDefs
	cout << "Prueba de TypeDefs" << endl << endl;

	//recorro los tipos y por cada tipo recorro todos los valores y verifico si cada uno de los valores coincide constituye un valor del tipo valido
	for(int tipoIndex = 0; tipoIndex < CANT_TIPOS; tipoIndex++)
		for(int valorIndex = 0; valorIndex < CANT_VALORES; valorIndex++)
			cout << (tipoIndex * CANT_VALORES + valorIndex + 1) << ") El valor '" << valores[valorIndex] << "' " << (tipos[tipoIndex]->esValorValido((void*)&valores[valorIndex])?"SI":"NO") << " es un " << tipos[tipoIndex]->getNombre() << " valido" << endl;

	//prueba de AttDefs
	cout << endl << endl << "Prueba de AttDefs" << endl << endl;

	//recorro los atributos y por cada uno recorro todos los valores para ver cual podria ser un valor del atributo
	for(int atIndex = 0; atIndex < CANT_ATRIBUTOS; atIndex++)
		for(int valorIndex = 0; valorIndex < CANT_VALORES; valorIndex++)
			cout << (atIndex * CANT_VALORES + valorIndex + 1) << ") El valor '" << valores[valorIndex] << "' " << (atributos[atIndex]->getTipo()->esValorValido((void*)&valores[valorIndex])?"SI":"NO") << " puede ser un valor del atributo " << atributos[atIndex]->getNombre() << " valido" << endl;
	
	//prueba de TagDef
	cout << endl << endl << "Prueba de TagDefs" << endl << endl;

	//muestro informacion del tag
	inspect(tagEscenario, 0);	

	//capturo una entrada solo para poder ver la pantalla
	cin >> aux;

	delete tagEscenario;
	return 0;
}